/*
    *@brief              :对文件加锁或者解锁，但是 flock()函数只能产生建议性锁

    *@param[fd]          :文件描述符，指定需要加锁的文件。

    *@param[cmd]         :F_SETLK 这种用法一般用于测试，测试调用进程对文件加一把由参数 flockptr 指向的 struct flock对象所描述的锁是否会加锁成功
                          F_SETLKW 对文件添加由 flockptr 指向的 struct flock 对象所描述的锁
                          F_GETLK 此命令是 F_SETLK 的阻塞版本（命令名中的 W 表示等待 wait），如果所请求的读锁或写锁因另一个进程当前已经对所请求区域的某部分进行了加锁，而导致请求失败，那么调用进程将会进入阻塞状态。 只有当请求的锁可用时，进程才会被唤醒。

                          F_GETLK 命令一般很少用， 事先用 F_GETLK 命令测试是否能够对文件加锁，然后再用 F_SETLK 或
                          F_SETLKW 命令对文件加锁，但这两者并不是原子操作， 所以即使测试结果表明可以加锁成功，但是在使
                          用 F_SETLK 或 F_SETLKW 命令对文件加锁之前也有可能被其它进程锁住。

    *@param[operation]  :对 struct flock 结构体说明如下：
                        - l_type： 所希望的锁类型，可以设置为 F_RDLCK、F_WRLCK 和 F_UNLCK 三种类型之一，
                            F_RDLCK表示共享性质的读锁， F_WRLCK 表示独占性质的写锁， F_UNLCK 表示解锁一个区域。

                        - l_whence 和 l_start： 这两个变量用于指定要加锁或解锁区域的起始字节偏移量，与 的 lseek()函数中的 offset 和 whence 参数相同，

                        - l_len： 需要加锁或解锁区域的字节长度。

                        - l_pid： 一个 pid，指向一个进程，表示该进程持有的锁能阻塞当前进程，当 cmd=F_GETLK 时有效。




    *@return            :成功将返回从文件头部开始算起的位置偏移量（字节为单位）， 也就是当前的读写位置； 发生错误将返回-1。

    *@others            :与flock区别：
                        -flock()仅支持对整个文件进行加锁/解锁；而 fcntl()可以对文件的某个区域（某部分内容）进行加锁解锁，可以精确到某一个字节数据。
                        -flock()仅支持建议性锁类型；而 fcntl()可支持建议性锁和强制性锁两种类型。

                        加锁注意事项：
                        - 锁区域可以在当前文件末尾处开始或者越过末尾处开始，但是不能在文件起始位置之前开始。
                        - 若参数 l_len 设置为 0，表示将锁区域扩大到最大范围，也就是说从锁区域的起始位置开始， 到文件的最大偏移量处（也就是文件末尾）都处于锁区域范围内。
                          而且是动态的， 这意味着不管向该文件追加写了多少数据，它们都处于锁区域范围，起始位置可以是文件的任意位置。
                        - 如果我们需要对整个文件加锁，可以将 l_whence 和 l_start 设置为指向文件的起始位置， 并且指定参数 l_len 等于 0。
*/

// int fcntl(int fd, int cmd, ... struct flock *flockptr );

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char *argv[])
{
    struct flock lock = {0};
    int fd = -1;
    char buf[] = "Hello World!";

    /* 校验传参 */
    if (2 != argc)
    {
        fprintf(stderr, "usage: %s <file>\n", argv[0]);
        exit(-1);
    }

    /* 打开文件 */
    fd = open(argv[1], O_WRONLY);
    if (-1 == fd)
    {
        perror("open error");
        exit(-1);
    }

    /* 对文件加锁 */
    lock.l_type = F_WRLCK;    //独占性写锁
    lock.l_whence = SEEK_SET; //文件头部
    lock.l_start = 0;         //偏移量为 0
    lock.l_len = 0;

    if (-1 == fcntl(fd, F_SETLK, &lock))
    {
        perror("加锁失败");
        exit(-1);
    }
    printf("对文件加锁成功!\n");

    /* 对文件进行写操作 */
    if (0 > write(fd, buf, strlen(buf)))
    {
        perror("write error");
        exit(-1);
    }

    /* 解锁 */ 
    lock.l_type = F_UNLCK; //解锁
    fcntl(fd, F_SETLK, &lock);
    printf("对文件解锁成功!\n");

    /* 退出 */
    close(fd);
    exit(0);
}